package robot

import (
	"context"
	"fmt"
	"io/ioutil"
	"os/exec"
	"strings"
	"time"

	"gitee.com/xuender/oils/sets"
	"gitee.com/xuender/oils/slices"
)

// Service 机器人服务.
type Service struct {
	commands []*Command
}

// NewService 新建机器人服务.
func NewService() *Service {
	s := &Service{
		commands: []*Command{},
	}

	s.Add(s.Help, "命令帮助信息", "帮助", "help", "h", "?")
	s.Add(s.shell, "Shell命令", "命令", "shell", "s", "!")

	return s
}

func (s *Service) shell(str string) string {
	args := GetArgs(str)
	if len(args) == 0 {
		return "缺少命令"
	}

	timeout := 5

	ctx, cancel := context.WithTimeout(context.Background(), time.Duration(timeout)*time.Second)
	defer cancel()
	// nolint: gosec
	cmd := exec.CommandContext(ctx, args[0], args[1:]...)

	stdout, err := cmd.StdoutPipe()
	if err != nil {
		return "错误:\n" + err.Error()
	}
	defer stdout.Close()

	if err := cmd.Start(); err != nil {
		return "错误:\n" + err.Error()
	}

	opBytes, err := ioutil.ReadAll(stdout)
	if err != nil {
		return "错误:\n" + err.Error()
	}

	return "命令返回:\n" + strings.TrimSpace(string(opBytes))
}

// Help 帮助信息.
func (s *Service) Help(str string) string {
	set := sets.Strings{}

	for _, arg := range GetArgs(str) {
		if c := s.find(arg); c != nil {
			set.Add(c.String())
		}
	}

	if len(set) == 0 {
		for _, c := range s.commands {
			set.Add(c.String())
		}
	}

	return fmt.Sprintf("帮助信息:\n\n%s\n", set.Join("\n"))
}

func (s *Service) find(name string) *Command {
	for _, c := range s.commands {
		for _, cn := range c.Names {
			if cn == name {
				return c
			}
		}
	}

	return nil
}

// Add 增加命令.
func (s *Service) Add(call func(string) string, usage string, names ...string) error {
	if len(names) == 0 {
		return ErrNoName
	}

	ns := slices.Strings(names)

	for _, c := range s.commands {
		if ns.Any(c.Names...) {
			return ErrNameOccupied
		}
	}

	s.commands = append(s.commands, &Command{
		Names: names,
		Call:  call,
		Usage: usage,
	})

	return nil
}

// Call 命令.
func (s *Service) Call(str string) string {
	str = DBCtoSBC(strings.TrimSpace(str))
	for _, c := range s.commands {
		if c.Hit(str) {
			return c.Call(str)
		}
	}

	return "你说的我不能理解"
}
